/*
 * @Author: fengshan0525 2735332396@qq.com
 * @Date: 2023-03-17 20:42:43
 * @LastEditors: asanupup 15465106+asanupup@user.noreply.gitee.com
 * @LastEditTime: 2025-07-30 11:17:33
 * @FilePath: \nest-template\src\menu\menu.service.ts
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE=
 */
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { AdminMenu } from '@libs/db/models/menu.model';
import { AmdinRole } from '@libs/db/models/role.model';
import { AdminRoleMenu } from '@libs/db/models/role_menu.model';
import _ from 'lodash';

@Injectable()
export class MenuService {
  constructor(
    @InjectModel('AdminMenu') private readonly menuModel: Model<AdminMenu>,
    @InjectModel('AmdinRole') private readonly roleModel: Model<AmdinRole>,
    @InjectModel('AdminRoleMenu')
    private readonly roleMenuModel: Model<AdminRoleMenu>,
  ) { }

  // 获取菜单、路由(处理后以children的形式返回),这个children是用来作为侧边栏显示的菜单
  /**
   * @breif: 首先判断用户是否具有管理员角色，如果是，则查找全部在启用的菜单并处理后返回
   * 如果用户是其他角色，则需要去角色表中查找用户所拥有的菜单，然后查询返回
   * 用户角色还细分为单个角色和多个角色，对应的逻辑也不一样
   * @param {*} body
   * @param {string} roles
   * @return {*}
   */
  async getMenusChildren(body, roles: string[]) {
    try {
      if (roles.includes('admin') || roles.includes('manage')) {
        const doc = await this.menuModel
          .find({}).sort({ order: 1 })
          .exec();
        return this.recursionFind(JSON.parse(JSON.stringify(doc)), 0);
      } else {
        // 该用户有多个角色，则需要将多个角色的菜单合并后返回
        if (roles.length > 1) {
          const customRoleIds: any[] = [];
          const menuIds: any[] = [];
          for (let i = 0; i < roles.length; i++) {
            const res = await this.roleModel.findOne({ roleKey: roles[i] });
            customRoleIds.push(res.roleId);
          }
          for (let i = 0; i < customRoleIds.length; i++) {
            const res = await this.roleMenuModel.findOne({
              roleId: customRoleIds[i],
            });
            menuIds.push(res.menus);
          }
          const flattenUnionArr = _.union(_.flatten(menuIds));
          if (flattenUnionArr.length) {
            const menus: any[] = [];
            for (let i = 0; i < flattenUnionArr.length; i++) {
              const result = await this.menuModel.findOne({
                menuId: flattenUnionArr[i],
                status: '1',
              });
              menus.push(result);
            }
            let notNullMenus = menus.filter(item => item).sort((a, b) => a.order - b.order)
            return this.recursionFind(JSON.parse(JSON.stringify(notNullMenus)), 0);
          } else {
            return [];
          }
        } else {
          // 单个角色
          const res = await this.roleModel.findOne({ roleKey: roles[0] });
          const menuIds = await this.roleMenuModel.findOne({
            roleId: res.roleId,
          });
          const menus: any[] = [];
          if (menuIds.menus.length) {
            for (let i = 0; i < menuIds.menus.length; i++) {
              const result = await this.menuModel.findOne({
                menuId: menuIds.menus[i],
                status: '1',
              });
              menus.push(result);
            }
            let notNullMenus = menus.filter(item => item).sort((a,b) => a.order-b.order)
            return this.recursionFind(JSON.parse(JSON.stringify(notNullMenus)), 0);
          }
        }
      }
    } catch (e) {
      throw new HttpException('未知错误', HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }
  /**
   * 递归处理菜单列表
   * @param doc 待处理数据
   * @param parentId 默认 0 表示最上层
   * @returns
   */
  recursionFind(doc: any[], parentId: number) {
    try {
      const children = doc.reduce((prev, item) => {
        if (item && item.parentId === parentId) {
          item.children = this.recursionFind(doc, item.menuId);
          prev.push(item);
        }
        return prev;
      }, []);
      return children.length ? children : undefined;
    } catch (e) {
      throw new HttpException(e, HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }

  /**
   * 构建菜单树形结构
   * @param menuList 菜单列表数据，来源于数据库查询结果
   * @param parentId 父级菜单ID，默认为0表示根节点
   * @returns 树形结构的菜单数据
   */
  buildMenuTree(menuList: any[], parentId: number = 0): any[] {
    try {
      // 筛选出当前父节点下的所有子节点
      const children = menuList
        .filter(item => item.parentId === parentId)
        .sort((a, b) => a.order - b.order); // 按order字段排序
      
      // 递归构建每个子节点的children
      return children.map(item => {
        const childNodes = this.buildMenuTree(menuList, item.menuId);
        return {
          ...item,
          children: childNodes.length > 0 ? childNodes : undefined
        };
      });
    } catch (e) {
      throw new HttpException('构建菜单树形结构失败', HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }

  /**
   * @breif: 获取所有菜单列表,并按照order排序
   * @return {*}
   */
  async getMenus(body) {
    try {
      const condition = [
        {
          $match: {
            $and: [
              { name: { $regex: body.name ?? '' } }
            ]
          }
        },
        {
          $facet: {
            total: [{ $count: 'count' }],
            rows: [
              { $skip: (body.pageNum - 1) * body.pageSize },
              { $limit: body.pageSize },
              { $project: { _id: 0, __v: 0 } },
            ],
          },
        },
        {
          $project: {
            list: '$rows',
            total: { $arrayElemAt: ['$total.count', 0] },
          },
        },
      ];

      const result = await this.menuModel.aggregate(condition);
      if (result[0]) {
        let list = result[0].list
        // 构建树形结构
        const treeMenus = this.buildMenuTree(list, 0);
        const res = {
          list: treeMenus,
          total: result[0].total
        }
        return res
      } else {
        return []
      }
    } catch (e) {
      if (e.status === 400) {
        throw new HttpException(e, HttpStatus.BAD_REQUEST);
      } else {
        throw new HttpException(
          '服务器内部错误',
          HttpStatus.INTERNAL_SERVER_ERROR,
        );
      }
    }
  }

  /**
   * @breif: 创建菜单|路由，如果新增菜单的parentId是0，则代表为主目录id
   * @param {AdminMenu} body
   * @return {*}
   */
  async createMenu(body: AdminMenu) {
    try {
      const category = await this.menuModel.find({ parentId: body.parentId });
      if (category?.length == 0) {
        body.menuId = body.parentId * 100 + 1;
      } else {
        let max = 0
        for (let i = 0; i < category.length; i++) {
          if (category[i].menuId > max) {
            max = category[i].menuId
          }
        }
        body.menuId = max + 1
      }

      body.statusDetail = body.status === '1' ? '启用' : '停用';
      const doc = await this.menuModel.create(body);
      doc.save();
      return '新增菜单成功';
    } catch (e) {
      if (e.status === 400) {
        throw new HttpException(e, HttpStatus.BAD_REQUEST);
      } else {
        throw new HttpException(
          '服务器内部错误',
          HttpStatus.INTERNAL_SERVER_ERROR,
        );
      }
    }
  }

  /**
   * @breif: 更新菜单
   * @param {AdminMenu} body
   * @return {*}
   */
  async updateMenu(body: AdminMenu) {
    try {
      body.statusDetail = body.status === '1' ? '启用' : '停用';
      await this.menuModel.updateOne({ menuId: body.menuId }, body);
      return '更新成功';
    } catch (e) {
      if (e.status === 400) {
        throw new HttpException(e, HttpStatus.BAD_REQUEST);
      } else {
        throw new HttpException(
          '服务器内部错误',
          HttpStatus.INTERNAL_SERVER_ERROR,
        );
      }
    }
  }

  /**
   * @breif: 删除菜单
   * @param {number} id 菜单id
   * @return {*}
   */
  async deleteMenu(id: number) {
    try {
      await this.menuModel.deleteOne({ menuId: id });
      return '删除成功';
    } catch (e) {
      if (e.status === 400) {
        throw new HttpException(e, HttpStatus.BAD_REQUEST);
      } else {
        throw new HttpException(
          '服务器内部错误',
          HttpStatus.INTERNAL_SERVER_ERROR,
        );
      }
    }
  }
  // 处理多个角色的情况
  private procRoles() {
    return 'ok';
  }
}
